
#include <cassert>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <iomanip>

#include "LruList.h"
#include "cache.h"



const int NUM_HASH_TABLE = 1;
const int FACT_TABLE_ITEM_SIZE = 64;
const int HASH_TABLE_ITEM_SIZE = 16;



const unsigned long fact_table_start_addr = 0;
const unsigned long fact_table_size = 1 * 1024 * 1024 * 1024 / CACHE_LINE_SIZE;
const unsigned long hash_table_start_addr = fact_table_start_addr + fact_table_size;
const unsigned long hash_table_size = 6 * 1024 * 1024 / CACHE_LINE_SIZE;


inline unsigned long get_random_addr(unsigned long start, unsigned long size){
	unsigned long offset = (rand() * rand() * rand()) % size;
	return start + offset;
}

long long simulate_single_simple_join (){
	long long cache_miss = 0;
	long long cache_access = 0;

	cache simulate_cache(NUM_CACHE_ENTRY, NUM_CACHE_SET);

	for(unsigned long index = hash_table_start_addr ; index < hash_table_start_addr + hash_table_size ; ++index){
		simulate_cache.access(index, HASH_TABLE_TUPLE);
	}

	unsigned long hash_item_addr;
	for(unsigned long cur_addr = fact_table_start_addr ; cur_addr < fact_table_start_addr + fact_table_size ; ++cur_addr){
		if(((cur_addr - fact_table_start_addr) % (fact_table_size / 100)) == 0){
			std::cout << "fact addr: " << cur_addr << " completed: " << std::setprecision(2) <<  ((cur_addr - fact_table_start_addr) / (double)fact_table_size * 100) 
				<<"% cache miss " << cache_miss << " cache miss rate: " << cache_miss / (double)cache_access * 100 <<"%\n";
			//simulate_cache.dump();
			simulate_cache.dump_each_color();
		}
		simulate_cache.access(cur_addr, FACT_TABLE_TUPLE);
		++cache_access;
		for(int i = 0 ; i < NUM_HASH_TABLE ; ++i){
			hash_item_addr = get_random_addr(hash_table_start_addr, hash_table_size);
			if(false == simulate_cache.access(hash_item_addr, HASH_TABLE_TUPLE)){
				++ cache_miss;
			}
			++cache_access;
		}
	}
	std::cout << std::endl;

	assert(cache_miss >= 0);
	return cache_miss;
}



long long simulate_norder_simple_join () {
	long long cache_miss = 0;
	long long cache_access = 0;

	cache simulate_cache(NUM_CACHE_ENTRY, NUM_CACHE_SET);

	for(unsigned long index = hash_table_start_addr ; index < hash_table_start_addr + hash_table_size ; ++index){
		simulate_cache.access(index, HASH_TABLE_TUPLE);
	}

	unsigned long hash_item_addr;
	unsigned long count = 0;
	for(int color = 0 ; color < NUM_COLOR ; ++color){
		simulate_cache.dump_each_color();
		for(unsigned long cur_addr = fact_table_start_addr ; cur_addr < fact_table_start_addr + fact_table_size ; ++cur_addr){
			if(get_color_num(cur_addr) != color){
				continue;
			}
			
			if(++count % (fact_table_size / 100) == 0){
				std::cout << "sequence count: " << count << " completed: " << std::setprecision(2) <<  (count / (double)fact_table_size * 100) 
					<<"% cache miss " << cache_miss << " cache miss rate: " << cache_miss / (double)cache_access * 100 <<"%\n";
				//simulate_cache.dump();
			}
			simulate_cache.access(cur_addr, FACT_TABLE_TUPLE);
			++cache_access;
			for(int i = 0 ; i < NUM_HASH_TABLE ; ++i){
				hash_item_addr = get_random_addr(hash_table_start_addr, hash_table_size);
				if(false == simulate_cache.access(hash_item_addr, HASH_TABLE_TUPLE)){
					++ cache_miss;
				}
				++cache_access;
			}
		}
	}

	return cache_miss;
}


int main () {
	srand ((unsigned int)time(NULL));
	//std::cout << simulate_single_simple_join() << std::endl;
	std::cout << simulate_norder_simple_join() << std::endl;

	return 0;
};